import typing
from datetime import datetime, timedelta
import time
from collections import namedtuple, deque, defaultdict, Counter
import struct
from functools import reduce, partial
import operator
from itertools import starmap, count, cycle, islice, repeat, takewhile, chain, groupby
import bisect

"""
（1）datetime模块
| %a | 工作日的英文缩写。　　　　　　　　　　　　　　　　　　　　| Sun, Mon, ...               |
| %A | 工作日的英文全称。　　　　　　　　　　　　　　　　　　　　| Sunday, Monday, ...         |
| %w | 以十进制数显示的工作日，其中零表示星期日，六表示星期六。　| 0, 1, ..., 6                |
| %d | 补零后，以十进制数显示的月份中的一天。　　　　　　　　　　| 01, 02, ..., 31             |
| %b | 月份的英文缩写。　　　　　　　　　　　　　　　　　　　　　| Jan, Feb, ...               |
| %B | 月份的英文全名。　　　　　　　　　　　　　　　　　　　　　| January, February, ...      |
| %m | 补零后，以十进制数显示的月份。　　　　　　　　　　　　　　| 01, 02, ..., 12             |
| %y | 补零后，以十进制数表示的，不带世纪的年份。　　　　　　　　| 00, 01, ..., 99             |
| %Y | 十进制数表示的带世纪的年份。　　　　　　　　　　　　　　　| 0001, ..., 2013, ..., 9999  |
| %H | 以补零后的十进制数表示的小时（二十四小时制）。　　　　　　| 00, 01, ..., 23             |
| %I | 以补零后的十进制数表示的小时（十二小时制）。　　　　　　　| 01, 02, ..., 12             |
| %p | 本地化的AM或PM 。　　　　　　　　　　　　　　　　　　　　| AM, PM                      |
| %M | 补零后，以十进制数显示的分钟。　　　　　　　　　　　　　　| 00, 01, ..., 59             |
| %S | 补零后，以十进制数显示的秒。　　　　　　　　　　　　　　　| 00, 01, ..., 59             |
| %f | 以十进制数表示的微秒，在左侧补零。　　　　　　　　　　　　| 000000, 000001, ..., 999999 |
| %Z | 时区名称（如果对象为简单型则为空字符串）。　　　　　　　　| UTC, GMT                    |
| %j | 以补零后的十进制数表示的一年中的日序号。　　　　　　　　　| 001, 002, ..., 366          |
| %c | 本地化的适当日期和时间表示。　　　　　　　　　　　　　　　| Tue Aug 17 11:29:23 2021    |
| %x | 本地化的适当日期表示。　　　　　　　　　　　　　　　　　　| 08/17/21                    |
| %X | 本地化的适当时间表示。　　　　　　　　　　　　　　　　　　| 11:29:23                    |
"""
# 初始化及表示
dt_now = datetime.now()  # 获取当前时间, 返回datetime对象，2021-08-17 11:29:23.697704
dt_now.date()  # 获取日期部分，返回date对象，2021-08-17
dt_now.time()  # 获取时间部分，返回time对象，11:29:23.697704
stamp = dt_now.timestamp()  # 获取时间戳，返回float对象，1629171453.481852
datetime.fromtimestamp(stamp)  # 时间戳转本地时间
datetime.utcfromtimestamp(stamp)  # 时间戳转UTC时间
dt_spec = datetime(2021, 12, 31, 23, 59, 59, 999999)  # 用指定日期时间创建datetime
dt_str = datetime.strptime('2021-08-17 11:29:23', '%Y-%m-%d %H:%M:%S')  # 根据字符串创建datetime
dt_str.strftime('%Y-%m-%d %H:%M:%S')  # 格式化字符串
# 时间加减，时间比较直接用><=
dt_spec - timedelta(days=1)  # 前一天
dt_spec + timedelta(days=1)  # 后一天


"""
（2）time模块
时间元组: time.struct_time(tm_year=2021, tm_mon=8, tm_mday=25, tm_hour=0, tm_min=13, tm_sec=38, tm_wday=2, tm_yday=237, tm_isdst=0)
"""
stamp = time.time()  # 返回当前时间的时间戳，格式为10位整数的浮点数。
struct_t = time.localtime()  # 将一个时间戳转换为当前时区的struct_time。如果未传参数，以当前时间为准
time.gmtime(stamp)  # 和localtime()方法类似，将一个时间戳转换为UTC时区（0时区）的struct_time
time.mktime(struct_t)  # 将一个struct_time转化为时间戳。
time.asctime(struct_t)  # 把一个struct_time表示为这种形式：'Sun Jun 20 23:21:05 1993'。如果未传参数，默认time.localtime()为参数
time.ctime(stamp)  # 把一个时间戳转化为time.asctime()的形式。如果未传参数，默认time.time()为参数。相当于time.asctime(time.localtime(secs))
format_t = time.strftime('%Y-%m-%d %H:%M:%S', struct_t)  # 将一个struct_time转为格式化字符串
time.strptime('2021-08-25 09:26:11', '%Y-%m-%d %H:%M:%S')  # 把一个格式化字符串转为struct_time。和strftime()互为逆操作

"""
（3）collections模块
"""
# 命名元组namedtuple
Point = namedtuple('Point', ('x', 'y'))
point = Point(1, 2)  # point.x，point.y

# 双向列表deque。list是线性的，访问元素很快但插入和删除元素很慢，deque是为了高效实现插入和删除操作的双向列表，适合用于队列和栈
q = deque(['b', 'c'])
q.append('d')
q.appendleft('a')

# 访问不存在的key时带默认值的字典defaultdict
dic = defaultdict(lambda: 'N/A')
dic['key1'] = 'a'  # dic['key2']返回'N/A'

# 有序字典OrderedDict，Key会按照插入的顺序排列
# ...

# Counter计数器,对可迭代对象中的元素进行统计
c = Counter('abbcccdddd')  # Counter({'d': 4, 'c': 3, 'b': 2, 'a': 1})

"""
（4）struct模块, 用于python数据和bytes之间的转换
在Python中，比方说把一个32位无符号整数变成字节，得配合位运算符实现，非常麻烦，如果换成浮点数就无能为力了。
    >>> n = 10240099
    >>> b1 = (n & 0xff000000) >> 24
    >>> b2 = (n & 0xff0000) >> 16
    >>> b3 = (n & 0xff00) >> 8
    >>> b4 = n & 0xff
    >>> bs = bytes([b1, b2, b3, b4])
Python提供了struct模块来解决bytes和其他二进制数据类型的转换。
1.pack(fmt,v1,v2…)：按照fmt把数据转换成bytes返回.
2.pack_into(fmt,buffer,offset,v1,v2…)：按照fmt将数据转换成bytes,并将字节流写入以offset开始的buffer中(buffer为可写的缓冲区,可用array模块)
3.unpack(fmt,v1,v2…)：按照fmt解析bytes,返回解析结果元组
4.pack_from(fmt,buffer,offset)：按照fmt解析以offset开始的缓冲区,并返回解析结果元组
5.calcsize(fmt)：计算给定的格式(fmt)占用多少字节的内存，注意对齐方式
格式化字符串fmt由对齐方式和格式符组成
1.对齐方式：
| Character | Byte_order  | Size | Alignment     |
| @(default)| 本机         | 本机  | 本机,凑够4字节  |
| =         | 本机         | 标准  | none,按原字节数 | 
| <         | 小端         | 标准  | none,按原字节数 |
| >         | 大端         | 标准  | none,按原字节数 |
| !         | network(大端)| 标准  | none,按原字节数 |
2.格式符:
| 符号 | C语言类型             | Python类型 | 字节大小   |
| c    | char               | bytes      | 1        |
| b    | signed_char        | integer    | 1        |
| B    | unsigned_char      | integer    | 1        |
| ?    | _Bool              | bool       | 1        |
| h    | short              | integer    | 2        |
| H    | unsigned_short     | integer    | 2        |
| i    | int                | integer    | 4        |
| I    | unsigned_int       | integer    | 4        |
| l    | long               | integer    | 4        |
| L    | unsigned_long      | long       | 4        |
| q    | long_long          | long       | 8        |
| Q    | unsigned_long_long | long       | 8        |
| f    | float              | float      | 4        |
| d    | double             | float      | 8        |
每个格式前可以有一个数字,表示这个类型的个数,如s格式表示一定长度的字符串,4s表示长度为4的字符串;4i表示四个int
"""
# 进制转化(2,8,16)
bin(1024)
oct(1024)
hex(1024)
# struct, fmt有多少个格式符, 不定长参数v也要有相应个数
struct.pack('>cb', b'1', 31)  # b'1\x1f'
struct.pack('>I', 10240099)  # b'\x00\x9c@c'
struct.unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80')  # (4042322160, 32896)
struct.calcsize('2if')  # 12，计算fmt表示的字节数
# 例子，bmp图片前26字节：BM(2byte)+位图大小(4byte)+保留位(4byte)+实际偏移量(4byte)+Header(4byte)+宽度(4byte)+高度(4byte)
bmp = b'BM\x9eX\x03\x00\x00\x00\x00\x006\x00\x00\x00(\x00\x00\x00\x0e\x01\x00\x00\x0e\x01\x00\x00'
struct.unpack('<ccIIIIII', bmp)  # (b'B', b'M', 219294, 0, 54, 40, 270, 270)，说明图片高宽为270px


"""
（5）内建函数
"""
# map函数,对可迭代对象都进行某个操作后返回相应的可迭代对象.可传入多个可迭代对象,有多少个可迭代对象传入的函数就应该有多少个参数
def add(x):
    return x+2
map_1 = map(lambda x: x+2, [1, 2, 3, 4])  # 使用lambda
map_2 = map(add, [1, 2, 3, 4])  # 不使用lambda
map_3 = map(lambda x, y: x+y, [1, 2, 3], [4, 5, 6])  # (1,4)(2,5)(3,6)依次传入,list结果为[5, 7, 9]

# filter函数,对可迭代对象进行过滤，返回过滤后的可迭代对象
filter_1 = filter(lambda x: x > 0, (-1, 2, -4, 3))  # list结果为[2, 3]
filter_2 = map(lambda x: x**2, filter(lambda x: x % 2 == 0, range(10)))  # 0-9中偶数的二次方


"""
（6）functools模块
"""
# reduce函数,它的第一个参数传递的函数要求有两个参数(x,y),x代表当前的操作结果,y代表下一个元素(初始x,y是前两个元素)
reduce_1 = reduce(lambda x, y: x + y, [1, 2, 3, 4])  # 返回所有元素相加之和
reduce_2 = reduce(operator.add, [1, 2, 3, 4])  # 等同于上面效果
reduce_3 = reduce(lambda x, y: y + x, 'abcd')  # 字符串反转,等效于'abcd'[::-1]

# partial函数,偏函数,用于固定住函数中的一个或多个参数
def my_add(x, y, z):
    return x+y+z
my_add_1 = partial(my_add, 1, 2)      # my_add_1(3)
my_add_2 = partial(my_add, y=2, z=3)  # my_add_2(1)
my_add_3 = partial(my_add, x=1, y=2)  # my_add_2(z=3)


"""
（7）itertools模块
"""
# starmap函数,是map()函数的*a版本
def my_sum(*s):
    return sum(s)
# (1,4)(2,5)(3,6)依次传入,list结果为[5, 7, 9],相当于map传入[1, 2, 3], [4, 5, 6]
smap_1 = starmap(lambda x, y: x+y, [(1, 4), (2, 5), (3, 6)])
smap_2 = starmap(my_sum, [(1, 4), (2, 5), (3, 6)])  # 和上面等效

# 自然数序列无限迭代器count
odd_num = count(1, 2)  # 从1开始，步长为2
# 循环无限迭代器cycle
cy = cycle('abc')  # a,b,c,a...
# 重复无限迭代器repeat
repeat('a', 10)  # 负责把一个元素无限重复下去，如果提供第二个参数可以限定重复次数
# 切片截取
islice(cy, 0, 10)
# 按条件截取
takewhile(lambda x: x < 10, cy)
# 连接多个可迭代对象
chain('abc', 'efg')
# 按元素分组,返回格式为Iterator[tuple[_S, Iterator[_T]]],此函数只会对相邻相同的元素视为同一组,无序的要先自己排序才能获得想要的结果
g = groupby(('a', 'b', 'c', 'c'))

"""
（8）bisect模块
用于有序序列的插入和查找
bisect.bisect(sorted, x)：返回x将会插入的位置。x在sorted中存在时，bisect_left是插入在相同值的左边,bisect_right右边
bisect.insort(sorted, x)：直接按序在sorted中插入x
"""
sorted_list = [1, 3, 5, 7]
bisect.bisect_left(sorted_list, 4)  # 返回2
bisect.insort_left(sorted_list, 4)  # sorted_list => [1, 3, 4, 5, 7]

